//////////////////////////////////////////////////////
// 程序名称：任意直线的对称变换
// 功 能：画任意对称轴，做对称变换
// 编译环境：Visual Studio 2017，EasyX_20190219(beta)
// 作 者：xyqlx<mxxyqlx@qq.com>
// 最后修改：2019-4-8
#include <conio.h>
#include <graphics.h>
#include <cmath>
#include <cstring>
#include <vector>

const float feps = 1e-8f;
int sgn(float f) { return f < -feps ? -1 : (f < feps ? 0 : 1); }

class Vector2
{
public:
	float mat[3];
	Vector2() : Vector2(0.0f, 0.0f) {}
	Vector2(float x, float y, float w = 1.0f)
	{
		mat[0] = x;
		mat[1] = y;
		mat[2] = w;
	}
	Vector2(const Vector2& rhs){
		mat[0] = rhs.mat[0];
		mat[1] = rhs.mat[1];
		mat[2] = rhs.mat[2];
	}
	const Vector2& operator=(const Vector2& rhs){
		mat[0] = rhs.mat[0];
		mat[1] = rhs.mat[1];
		mat[2] = rhs.mat[2];
		return *this;
	}
	float Norm() const { return sqrt(mat[0] * mat[0] + mat[1] * mat[1]); }
	bool Normalize()
	{
		float r = Norm();
		if (sgn(r))
		{
			mat[0] /= r;
			mat[1] /= r;
			return 1;
		}
		return 0;
	}
};
Vector2 operator+(const Vector2 &lhs, const Vector2 &rhs)
{
	return Vector2(lhs.mat[0] + rhs.mat[0], lhs.mat[1] + rhs.mat[1], lhs.mat[2]);
}
Vector2 operator-(const Vector2 &lhs, const Vector2 &rhs)
{
	return Vector2(lhs.mat[0] - rhs.mat[0], lhs.mat[1] - rhs.mat[1], lhs.mat[2]);
}
Vector2 operator-(const Vector2 &rhs)
{
	return Vector2(-rhs.mat[0], -rhs.mat[1], rhs.mat[2]);
}
float Distance(const Vector2 &lhs, const Vector2 &rhs)
{
	return sqrt(pow(lhs.mat[0] - rhs.mat[0], 2) + pow(lhs.mat[1] - rhs.mat[1], 2));
}

class Transform2
{
public:
	float mat[3][3];
	Transform2()
	{
		memset(mat, 0, sizeof(mat));
		mat[0][0] = mat[1][1] = mat[2][2] = 1.0f;
	}
	Transform2(const Transform2 &rhs)
	{
		memcpy(mat, rhs.mat, sizeof(mat));
	}
	Transform2 &operator=(const Transform2 &rhs)
	{
		memcpy(mat, rhs.mat, sizeof(mat));
	}
	const Transform2 &operator*=(const Transform2 &rhs)
	{
		float res[3][3];
		memset(res, 0, sizeof(res));
		for (int i = 0; i < 3; ++i)
		{
			for (int j = 0; j < 3; ++j)
			{
				for (int k = 0; k < 3; ++k)
				{
					res[i][j] += mat[i][k] * rhs.mat[k][j];
				}
			}
		}
		memcpy(mat, res, sizeof(mat));
		return *this;
	}
};
Transform2 operator*(const Transform2 &lhs, const Transform2 &rhs)
{
	Transform2 temp(lhs);
	temp *= rhs;
	return temp;
}
Transform2 TMultiply(const Transform2 &lhs, const Transform2 &rhs)
{
	return lhs * rhs;
}
Vector2 operator*(const Vector2 &lhs, const Transform2 &rhs)
{
	Vector2 res(0.0f, 0.0f, 0.0f);
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			res.mat[i] += lhs.mat[j] * rhs.mat[j][i];
		}
	}
	return res;
}
Vector2 VMultiply(const Vector2 &lhs, const Transform2 &rhs)
{
	return lhs * rhs;
}
const Vector2 &operator*=(Vector2 &lhs, const Transform2 &rhs)
{
	float mat[3];
	memset(mat, 0, sizeof(mat));
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			mat[i] += lhs.mat[j] * rhs.mat[j][i];
		}
	}
	memcpy(lhs.mat, mat, sizeof(mat));
	return lhs;
}
class Transform2Factory
{
public:
	Transform2 Trivial(){
		Transform2 res;
		return res;
	}
	Transform2 Translation(const Vector2 &d)
	{
		Transform2 res;
		res.mat[2][0] = d.mat[0];
		res.mat[2][1] = d.mat[1];
		return res;
	}
	Transform2 Translation(float dx, float dy)
	{
		Transform2 res;
		res.mat[2][0] = dx;
		res.mat[2][1] = dy;
		return res;
	}
	Transform2 Rotation(Vector2 angle)
	{
		Transform2 res;
		if (angle.Normalize())
		{
			res.mat[0][0] = res.mat[1][1] = angle.mat[0];
			res.mat[0][1] = angle.mat[1];
			res.mat[1][0] = -angle.mat[1];
		}
		return res;
	}
	Transform2 Rotation(float angle)
	{
		Transform2 res;
		float c = cos(angle);
		float s = sin(angle);
		res.mat[0][0] = res.mat[1][1] = c;
		res.mat[0][1] = s;
		res.mat[1][0] = -s;
		return res;
	}
	Transform2 Scaling(const Vector2 &s)
	{
		Transform2 res;
		res.mat[0][0] = s.mat[0];
		res.mat[1][1] = s.mat[1];
		return res;
	}
	Transform2 Scaling(float sx, float sy)
	{
		Transform2 res;
		res.mat[0][0] = sx;
		res.mat[1][1] = sy;
		return res;
	}
	Transform2 SymmetryX()
	{
		Transform2 res;
		res.mat[1][1] = -1;
		return res;
	}
	Transform2 SymmetryY()
	{
		Transform2 res;
		res.mat[0][0] = -1;
		return res;
	}
	Transform2 Shear(float sx,float sy){
		Transform2 res;
		res.mat[1][0] = sx;
		res.mat[0][1] = sy;
		return res;
	}
	Transform2 SymmetryLine(Vector2 v1, Vector2 v2)
	{
		Transform2 res;
		if (!sgn(v1.mat[0] - v2.mat[0]))
		{
			if (!sgn(v1.mat[1] - v2.mat[1]))
			{
				return res;
			}
			res *= Translation(-v1.mat[0], 0);
			res *= SymmetryY();
			res *= Translation(v1.mat[0], 0);
			return res;
		}
		else if (!sgn(v1.mat[1] - v2.mat[1]))
		{
			res *= Translation(0, -v1.mat[1]);
			res *= SymmetryX();
			res *= Translation(0, v1.mat[1]);
			return res;
		}
		res *= Translation(-v1);
		res *= Rotation((v2 - v1) * SymmetryX());
		res *= SymmetryX();
		res *= Rotation(v2 - v1);
		res *= Translation(v1);
		return res;
	}
};

class Shape
{
public:
	Shape(int n) : stateNum(n) {}
	virtual void Redraw() const = 0;
	int stateNum;
	virtual void State(int &state, int x, int y) = 0;
	virtual const Shape &Multiply(const Transform2 &rhs) = 0;
	virtual ~Shape(){};
};

class Line : public Shape
{
public:
	Vector2 v1, v2;
	COLORREF lineColor;
	Line() : Shape(4) {}
	Line(int x1, int y1, int x2, int y2, COLORREF lineColor = getlinecolor()) : Shape(4), v1(x1, y1), v2(x2, y2), lineColor(lineColor) {}
	virtual void Redraw() const
	{
		setlinecolor(lineColor);
		line(v1.mat[0], v1.mat[1], v2.mat[0], v2.mat[1]);
	}
	virtual void State(int &state, int x, int y)
	{
		switch (state)
		{
		case 1:
			v1.mat[0] = x;
			v1.mat[1] = y;
			break;
		case 2:
			v2.mat[0] = x;
			v2.mat[1] = y;
			lineColor = getlinecolor();
		case 3:
			line(v1.mat[0], v1.mat[1], v2.mat[0], v2.mat[1]);
			break;
		}
	}
	const Shape &Multiply(const Transform2 &rhs)
	{
		v1 *= rhs;
		v2 *= rhs;
		return *this;
	}
	virtual ~Line() {}
};
const Line &operator*=(Line &lhs, const Transform2 &rhs)
{
	lhs.v1 *= rhs;
	lhs.v2 *= rhs;
	return lhs;
}

class LineSet
{
  public:
	std::vector<Line> vec;
	LineSet() {}
	LineSet(const std::vector<int> &lines)
	{
		int n = lines.size() / 4;
		for (int i = 0; i < n; ++i)
		{
			vec.push_back(Line(lines[4 * i], lines[4 * i + 1], lines[4 * i + 2], lines[4 * i + 3]));
		}
	}
	LineSet(const LineSet &rhs)
	{
		vec = rhs.vec;
	}
	void SetColor(COLORREF color)
	{
		for (std::vector<Line>::iterator it = vec.begin(); it != vec.end(); ++it)
			it->lineColor = color;
	}
	void AddLine(const Line &rhs)
	{
		vec.push_back(rhs);
	}
	void Redraw() const
	{
		for (std::vector<Line>::const_iterator it = vec.begin(); it != vec.end(); ++it)
			it->Redraw();
	}
	const LineSet &operator*=(const Transform2 &rhs)
	{
		for (std::vector<Line>::iterator it = vec.begin(); it != vec.end(); ++it)
			it->Multiply(rhs);
		return *this;
	}
	LineSet operator*(const Transform2 &rhs) const
	{
		LineSet temp;
		temp.vec = vec;
		for (std::vector<Line>::iterator it = temp.vec.begin(); it != temp.vec.end(); ++it)
			it->Multiply(rhs);
		return temp;
	}
};

IMAGE img(640, 480);

void Refresh()
{
	SetWorkingImage();
	putimage(0, 0, &img, SRCCOPY);
}

//变换工厂
Transform2Factory factory;
//沿线段为轴对称
Transform2 QuickSymmetryLine(const Vector2& v1,const Vector2& v2){
	Transform2 res;
	if(v1.mat[1] == v2.mat[1]){
		if(v1.mat[0]==v2.mat[0])
			return res;
		res.mat[1][1] = -1;
		res.mat[2][1] = 2 * v1.mat[1];
		return res;
	}
	int tx = -v1.mat[0]-v1.mat[1]*(v1.mat[0]-v2.mat[0])/(v2.mat[1]-v1.mat[1]);
	Vector2 vec(v1.mat[0] - v2.mat[0], v1.mat[1] - v2.mat[1]);
	vec.Normalize();
	float c2 = vec.mat[0] * vec.mat[0] - vec.mat[1] * vec.mat[1];
	float s2 = 2 * vec.mat[0] * vec.mat[1];
	res.mat[0][0] = c2;
	res.mat[0][1] = s2;
	res.mat[1][0] = s2;
	res.mat[1][1] = -c2;
	res.mat[2][0] = float(tx) * (c2 - 1);
	res.mat[2][1] = float(tx) * s2;
	return res;
}


//用户视图变换
Transform2 userView(factory.SymmetryX() * factory.Translation(320, 240));
Transform2 i_userView(factory.Translation(-320, -240)*factory.SymmetryX());
void Display()
{
	//初始化图案
	std::vector<int> shapeInfo = {-3,-13,-3,4,-3,4,-11,4,-11,4,1,18,1,18,13,4,13,4,4,4,4,4,3,-15,3,-15,-3,-13};
	LineSet ls(shapeInfo);
	SetWorkingImage(&img);
	(ls*userView).Redraw();
	//初始化
	Line currentLine;
	MOUSEMSG msg;
	int cnt = 0;
	//开始绘图
	Refresh();
	while (1)
	{
		msg = GetMouseMsg();
		switch (msg.uMsg)
		{
		case WM_MOUSEMOVE:
			if (cnt)
			{
				Refresh();
				currentLine.State(cnt, msg.x, msg.y);
			}
			break;
		case WM_LBUTTONDOWN:
			Refresh();
			setlinecolor(RED);
			currentLine.State(++cnt, msg.x, msg.y);
			if (++cnt >= currentLine.stateNum)
			{
				//(ls*factory.SymmetryLine(currentLine.v1*i_userView, currentLine.v2*i_userView)*userView).Redraw();
				(ls*QuickSymmetryLine(currentLine.v1*i_userView, currentLine.v2*i_userView)*userView).Redraw();
				cnt = 0;
			}
			break;
		case WM_RBUTTONDOWN:
			return;
		}
	}
}

int main()
{
	// 创建绘图窗口，大小为 640x480 像素
	initgraph(640, 480);
	//填充背景为白色
	setbkcolor(WHITE);
	cleardevice();
	//坐标轴
	SetWorkingImage(&img);
	setbkcolor(WHITE);
	cleardevice();
	setlinecolor(GREEN);
	line(0, 240, 640, 240);
	line(320, 0, 320, 480);
	//开始绘制
	setlinecolor(RED);
	Display();
	//结束绘制
	_getch();			// 按任意键继续
	closegraph(); // 关闭绘图窗口
	return 0;
}